import * as YvanUI from './YvanUIExtend'
import * as YvanMessage from './YvanUIMessage'
import {Ajax} from './YvanUIAjax'
import {
    GridDataSource,
    GridDataSourceSql,
    GridDataSourceServer,
    GridDataSourceInvoke,
    GridDataSourcePost,
    GridDataSourceAjax,
    GridDataSourceStaticFunction,
    WatchParam,
    GetParam,
    GridOnBeforeArgs,
    GridOnCUDBeforeArgs
} from './YvanDataSourceGrid'
import {brokerInvoke, brokerPost} from './Service'
import {Db} from './YvanUIDb'
import {YvEventDispatch} from './YvanEvent'
import _ from 'lodash'

export class YvanDataSourceGrid {
    private option: GridDataSource
    private ctl: any
    private readonly module: any
    private watches: Function[] = []
    private isFirstAutoLoad: boolean = true //是否为第一次自动读取

    private reload: undefined | (() => void)
    private rowCount: number | undefined
    public lastFilterModel: any
    public lastSortModel: any

    serverQuery = _.debounce((option: GridDataSourceSql | GridDataSourceServer | GridDataSourceInvoke | GridDataSourcePost | GridDataSourceAjax, paramFunction: undefined | (() => any), params: any) => {
        const that = this

        let needCount = false
        if (typeof that.rowCount === 'undefined') {
            //从来没有统计过 rowCount(记录数)
            needCount = true
            that.lastFilterModel = _.cloneDeep(params.filterModel)
            that.lastSortModel = _.cloneDeep(params.sortModel)
        } else {
            if (!_.isEqual(that.lastFilterModel, params.filterModel)) {
                //深度对比，如果 filter 模型更改了，需要重新统计 rowCount(记录数)
                needCount = true
                that.lastFilterModel = _.cloneDeep(params.filterModel)
                that.lastSortModel = _.cloneDeep(params.sortModel)
            }
        }

        // 获取所有参数
        const queryParams = {
            ...(typeof paramFunction === 'function' ? paramFunction() : undefined)
        }

        let ajaxPromise: Promise<Db.Response>;
        if (option.type === 'SQL') {
            const ajaxParam = {
                params: queryParams,
                limit: params.endRow - params.startRow,
                limitOffset: params.startRow,
                needCount,
                sortModel: params.sortModel,
                filterModel: params.filterModel,
                sqlId: option.sqlId
            };

            const beforeArgs: GridOnBeforeArgs = {
                requestParam: ajaxParam,
                handle: false,
                successCallback: params.successCallback,
                failCallback: params.failCallback
            }

            YvEventDispatch(option.onBefore, that.ctl, beforeArgs)
            if (beforeArgs.handle === true) {
                // 不允许请求
                return;
            }
            ajaxPromise = YvanUI.dbs[option.db].query(ajaxParam)

        } else if (option.type === 'Server') {
            const [serverUrl, method] = _.split(option.method, '@');
            const ajaxParam = {
                params: queryParams,
                limit: params.endRow - params.startRow,
                limitOffset: params.startRow,
                needCount,
                sortModel: params.sortModel,
                filterModel: params.filterModel,
            }

            const beforeArgs: GridOnBeforeArgs = {
                requestParam: ajaxParam,
                handle: false,
                successCallback: params.successCallback,
                failCallback: params.failCallback
            }

            YvEventDispatch(option.onBefore, that.ctl, beforeArgs)
            if (beforeArgs.handle === true) {
                // 不允许请求
                return;
            }
            ajaxPromise = <Promise<Db.Response>>brokerInvoke(YvanUI.getServerPrefix(serverUrl), method, ajaxParam)

        } else if (option.type === 'Invoke') {
            const [serverUrl, method] = _.split(option.method, '@');
            queryParams.page = Number(params.currentPage);
            queryParams.limit = Number(params.pageSize);
            const ajaxParam = {
                params: queryParams,
                sortModel: params.sortModel,
                filterModel: params.filterModel,
            }

            const beforeArgs: GridOnBeforeArgs = {
                requestParam: ajaxParam,
                handle: false,
                successCallback: params.successCallback,
                failCallback: params.failCallback
            }

            YvEventDispatch(option.onBefore, that.ctl, beforeArgs)
            if (beforeArgs.handle === true) {
                // 不允许请求
                return;
            }
            ajaxPromise = <Promise<Db.Response>>brokerInvoke(YvanUI.getServerPrefix(serverUrl), method, ajaxParam)

        } else if (option.type === 'Post') {
            queryParams.page = Number(params.currentPage);
            queryParams.limit = Number(params.pageSize);
            const ajaxParam = {
                params: queryParams,
                sortModel: params.sortModel,
                filterModel: params.filterModel,
            }
            // 更改用于表头排序的字段
            if (ajaxParam.sortModel.length == 1) {
                let colId = ajaxParam.sortModel[0].colId
                that.ctl.columns.forEach(function (col: any) {
                    if (col.field === colId) {
                        if (col.nameTable) {
                            ajaxParam.sortModel[0].colId = col.nameTable
                        } else {
                            ajaxParam.sortModel[0].colId = col.field
                        }
                    }
                });
            }

            const beforeArgs: GridOnBeforeArgs = {
                requestParam: ajaxParam,
                handle: false,
                successCallback: params.successCallback,
                failCallback: params.failCallback
            }

            YvEventDispatch(option.onBefore, that.ctl, beforeArgs)
            if (beforeArgs.handle === true) {
                // 不允许请求
                return;
            }
            ajaxPromise = <Promise<Db.Response>>brokerPost(option.method, ajaxParam)

        } else if (option.type === 'Ajax') {
            const ajax: Ajax.Function = _.get(window, 'YvanUI.ajax');
            const ajaxParam: Ajax.Option = {
                url: option.url,
                method: 'POST-JSON',
                data: {
                    params: queryParams,
                    limit: params.endRow - params.startRow,
                    limitOffset: params.startRow,
                    needCount,
                    sortModel: params.sortModel,
                    filterModel: params.filterModel,
                }
            }

            const beforeArgs: GridOnBeforeArgs = {
                requestParam: ajaxParam,
                handle: false,
                successCallback: params.successCallback,
                failCallback: params.failCallback
            }

            YvEventDispatch(option.onBefore, that.ctl, beforeArgs)
            if (beforeArgs.handle === true) {
                // 不允许请求
                return;
            }
            ajaxPromise = <Promise<Db.Response>>ajax(ajaxParam);

        } else {
            console.error('unSupport dataSource mode:', option);
            params.failCallback();
            return;
        }

        if (that.ctl.loading_data == true) {
            return;
        }
        //异步请求数据内容
        that.ctl.loading = true
        that.ctl.loading_data = true
        ajaxPromise.then(res => {
            YvEventDispatch(option.onAfter, that.ctl, res)

            const {data: resultData, pagination, params: resParams} = res
            if (needCount) {
                if (_.has(res, 'totalCount')) {
                    // 兼容老模式
                    that.rowCount = _.get(res, 'totalCount');

                } else {
                    that.rowCount = pagination.total
                }
            }
            params.successCallback(resultData, that.rowCount)
            /** 如果不分页就在这里设置总条目数量，避免多次刷新分页栏 **/
            if (!that.ctl.pagination) {
                that.ctl.gridPage.itemCount = that.rowCount
            }
            that.ctl._bindingComplete()
            if (that.ctl.entityName) {
                _.set(that.module, that.ctl.entityName + '.selectedRow', that.ctl.getSelectedRow())
            }

        }).catch(r => {
            params.failCallback()

        }).finally(() => {
            that.ctl.loading_data = false
            this.ctl.loading = false
        })
    })

    /**
     * SQL取值
     */
    setSqlMode(option: GridDataSourceSql | GridDataSourceServer | GridDataSourceInvoke | GridDataSourcePost | GridDataSourceAjax, paramFunction: undefined | (() => any)) {
        const that = this
        if (that.ctl._module.isDesignMode === true) {
            return;
        }
        this.reload = () => {
            this.ctl.loading = true
            that.clearRowCount()
            if (that.ctl.entityName) {
                _.set(that.module, that.ctl.entityName + '.selectedRow', undefined)
            }
            that.ctl.gridApi.hasDataSource = true

            if (that.ctl.pagination) {
                /** 分页模式 **/
                that.ctl.gridPage.getPageData = (currentPage: number, pageSize: number) => {
                    let params: any = {}
                    params.successCallback = (data: [], rowCount: number) => {

                        // if (needClearRefresh) {
                        //   that.ctl.setData(data)

                        // } else {
                        // 不能直接用 setData, 会造成 filter 被置空
                        // 使用 _transactionUpdate 也有 bug ，如果查询条件被改变，也不会分页回顶端
                        that.ctl._transactionUpdate(data)
                        // }
                        // that.ctl.setData(data)
                        that.ctl.gridPage.itemCount = rowCount
                        that.ctl.gridPage.currentPage = currentPage
                        that.ctl._bindingComplete();
                    }
                    params.failCallback = () => {
                        console.error('error')
                    }
                    // 增加分页参数校验
                    if (isNaN(currentPage)) {
                        currentPage = 1;
                    }
                    params.startRow = (currentPage - 1) * pageSize
                    params.endRow = currentPage * pageSize
                    params.currentPage = currentPage
                    params.pageSize = pageSize
                    params.filterModel = that.ctl.gridApi.getFilterModel()
                    params.sortModel = that.ctl.gridApi.getSortModel()

                    if (that.isFirstAutoLoad && that.ctl.autoLoad === false) {
                        that.rowCount = 0
                        params.successCallback([], that.rowCount)
                        that.ctl.loading = false
                        that.isFirstAutoLoad = false

                    } else {
                        that.serverQuery(option, paramFunction, params)
                    }
                }
                that.ctl.gridPage.getPageData(1, that.ctl.gridPage.pageSize);
            } else {
                /** 无限滚动模式 **/
                that.ctl.gridApi.setDatasource({
                    getRows: (params: any) => {
                        if (that.isFirstAutoLoad && that.ctl.autoLoad === false) {
                            that.rowCount = 0
                            params.successCallback([], that.rowCount)
                            that.ctl.loading = false
                            that.isFirstAutoLoad = false
                            return
                        }

                        that.serverQuery(option, paramFunction, params)
                    }
                })
            }
        }

        this.reload()
    }

    /**
     * 自定义函数式取值
     */
    setCustomFunctionMode(option: GridDataSourceStaticFunction, paramFunction: undefined | (() => any)) {
        const that = this
        this.reload = () => {
            that.clearRowCount()
            if (that.ctl.entityName) {
                _.set(that.module, that.ctl.entityName + '.selectedRow', undefined)
            }
            that.ctl.loading = true

            if (that.ctl.pagination) {
                that.ctl.gridPage.getPageData = (currentPage: number, pageSize: number) => {
                    let params: any = {}
                    params.successCallback = (data: [], rowCount: number) => {
                        // if (needClearRefresh) {
                        //   that.ctl.setData(data)

                        // } else {
                        // 不能直接用 setData, 会造成 filter 被置空
                        // 使用 _transactionUpdate 也有 bug ，如果查询条件被改变，也不会分页回顶端
                        that.ctl._transactionUpdate(data)
                        // }
                        // that.ctl.setData(data)
                        that.ctl.gridPage.itemCount = rowCount
                        that.ctl.gridPage.currentPage = currentPage
                    }
                    params.failCallback = () => {
                        console.error('error')
                    }
                    params.startRow = (currentPage - 1) * pageSize
                    params.endRow = currentPage * pageSize
                    params.filterModel = that.ctl.gridApi.getFilterModel()
                    params.sortModel = that.ctl.gridApi.getSortModel()

                    if (that.isFirstAutoLoad && that.ctl.autoLoad === false) {
                        that.rowCount = 0
                        params.successCallback([], that.rowCount)
                        that.ctl.loading = false
                        that.isFirstAutoLoad = false

                    } else {
                        option.call(that.module, that.ctl, {
                            param:
                                typeof paramFunction === 'function' ? paramFunction() : undefined,

                            failCallback: () => {
                                params.failCallback()
                            },

                            successCallback: (data: any[], dataLength: number | undefined) => {
                                if (!dataLength) {
                                    dataLength = data.length
                                }
                                if (params.startRow === 0 && dataLength <= params.endRow) {
                                    params.successCallback(data, dataLength)
                                } else {
                                    params.successCallback(_.slice(data, params.startRow, dataLength), dataLength)
                                }

                                that.ctl.loading = false
                                that.ctl.gridPage.itemCount = dataLength
                                that.ctl._bindingComplete()
                                if (that.ctl.entityName) {
                                    _.set(
                                        that.module,
                                        that.ctl.entityName + '.selectedRow',
                                        that.ctl.getSelectedRow()
                                    )
                                }
                            }
                        })
                    }
                }
                that.ctl.gridPage.getPageData(1, that.ctl.gridPage.pageSize);
            } else {
                // rowModelType = infinite
                that.ctl.gridApi.setDatasource({
                    getRows: (params: any) => {
                        that.ctl.loading = true

                        if (that.isFirstAutoLoad && that.ctl.autoLoad === false) {
                            that.rowCount = 0
                            params.successCallback([], that.rowCount)
                            that.ctl.loading = false
                            that.isFirstAutoLoad = false
                            return
                        }

                        option.call(that.module, that.ctl, {
                            param:
                                typeof paramFunction === 'function' ? paramFunction() : undefined,

                            failCallback: () => {
                                params.failCallback()
                            },

                            successCallback: (data: any[], dataLength: number | undefined) => {
                                if (!dataLength) {
                                    dataLength = data.length
                                }
                                if (params.startRow === 0 && dataLength <= params.endRow) {
                                    params.successCallback(data, dataLength)
                                } else {
                                    params.successCallback(_.slice(data, params.startRow, dataLength), dataLength)
                                }

                                that.ctl.loading = false
                                that.ctl.gridPage.itemCount = dataLength
                                that.ctl._bindingComplete()
                                if (that.ctl.entityName) {
                                    _.set(
                                        that.module,
                                        that.ctl.entityName + '.selectedRow',
                                        that.ctl.getSelectedRow()
                                    )
                                }
                            }
                        })
                    }
                })
            }
        }

        this.reload()
    }

    setCodeArrayMode(option: Array<any>) {
        const that = this
        const rowCount = option.length
        this.reload = () => {
            this.ctl.loading = true
            that.clearRowCount()
            if (that.ctl.entityName) {
                _.set(that.module, that.ctl.entityName + '.selectedRow', undefined)
            }
            that.ctl.gridApi.hasDataSource = true

            if (that.ctl.pagination) {
                /** 分页模式 **/

                that.ctl.gridPage.getPageData = (currentPage: number, pageSize: number) => {
                    let d: any[] = []
                    const startRow = (currentPage - 1) * pageSize
                    let endRow = currentPage * pageSize
                    endRow = endRow > rowCount ? rowCount : endRow

                    for (let i = startRow; i < endRow; i++) {
                        d.push(option[i])
                    }
                    that.ctl.setData(d)
                    that.ctl.gridPage.itemCount = rowCount
                    that.ctl.gridPage.currentPage = currentPage
                }

                that.ctl.gridPage.getPageData(1, that.ctl.gridPage.pageSize)
            } else {
                /** 不分页模式 **/
                that.ctl.setData(option)
                that.ctl.gridPage.itemCount = rowCount
            }
        }

        this.reload()
    }

    constructor(ctl: any, option: GridDataSource) {
        if (ctl._webix.$scope.isDesignMode === true) {
            return
        }
        this.ctl = ctl
        this.option = option
        this.module = ctl._webix.$scope

        if (!option) {
            //没有设值，退出
            this.reload = undefined
            return
        }

        if (_.isArray(option)) {
            this.setCodeArrayMode(option)
            return
        }

        if (typeof option === 'function') {
            //以 function 方式运行
            this.setCustomFunctionMode(option, undefined)
            return
        }

        // 使 watch 生效
        _.forOwn(option.params, value => {
            if (!_.has(value, '$watch')) {
                return
            }
            const watchOption: WatchParam = value
            this.module.$watch(watchOption.$watch, () => {
                if (this.reload) {
                    ctl.reload(2);
                }
            })
        })

        // params 函数
        let paramFunction = () => {
            const result: any = {}
            _.forOwn(option.params, (value, key) => {
                if (_.has(value, '$get')) {
                    const getOption: GetParam = value
                    result[key] = _.get(this.module, getOption.$get)
                } else if (_.has(value, '$watch')) {
                    const watchOption: WatchParam = value
                    result[key] = _.get(this.module, watchOption.$watch)
                } else {
                    result[key] = value
                }
            })
            //@ts-ignore
            _.forOwn(option.extParams, (v, k) => {
                if (_.isFunction(v)) {
                    result[k] = v();
                }else{
                    result[k] = v;
                }
            })
            return result
        }

        if (option.type === 'function') {
            if (typeof option.bind === 'function') {
                this.setCustomFunctionMode(option.bind, paramFunction)
            } else {
                // 取 bind 函数
                const bindFunction: GridDataSourceStaticFunction = _.get(this.module, option.bind) as GridDataSourceStaticFunction
                if (!bindFunction) {
                    console.error(`没有找到名称为 ${option.bind} 的方法`)
                    return
                }
                this.setCustomFunctionMode(bindFunction, paramFunction)
            }
            return
        }

        if (option.type === 'SQL' || option.type === 'Server' || option.type === 'Invoke' || option.type === 'Post' || option.type === 'Ajax') {
            this.setSqlMode(option, paramFunction)
            return
        }

        console.error(`其他方式没有实现`)
    }

    /**
     * 释放与 YvGrid 的绑定
     */
    destory() {
        // 解除全部 watch
        _.each(this.watches, unwatch => {
            unwatch()
        })
        this.reload = undefined
    }

    /**
     * 清空 rowCount, 下次重新统计总行数
     */
    clearRowCount() {
        delete this.rowCount
    }

    updateSupport(): boolean {
        return false
    }

    _updateRow(param: any) {
        const option: any = this.option
        if (!option.updatePath) {
            param.node.cstate = 'same'
            return;
            // throw new Error('no updatePath')
        }
        const [serverUrl, method] = _.split(option.updatePath, '@');

        const wheres = {}
        _.set(wheres, this.ctl.idField, this.ctl._getIdByRow(param.data))

        const sets = {}
        _.forEach(this.ctl.vjson.columns, column => {
            if (column.field != this.ctl.idField) {
                const newValue = _.get(param.data, column.field)
                const oldValue = _.get(param.node.origin, column.field)
                if (newValue != oldValue) {
                    if (column.editMode === "date" || column.editMode === 'datetime') {
                        let formatter = column.formatter
                        if (!YvanUI.formatter.hasOwnProperty(column.formatter)) {
                            console.error('没有发现全局函数 YvanUI.formatter.' + column.formatter)
                            _.set(sets, column.field, newValue)
                        } else {
                            formatter = YvanUI.formatter[column.formatter]
                            const dateValue = formatter(newValue)
                            if (dateValue) {
                                _.set(sets, column.field, dateValue)
                            } else {
                                _.set(sets, column.field, newValue)
                            }
                        }
                    } else {
                        if (newValue) {
                            _.set(sets, column.field, newValue)
                        }
                    }
                }
            }
        })

        if (_.size(sets) <= 0) {
            param.node.cstate = 'same'
            return
        }

        const ajaxParam = {
            args: [wheres, sets]
        }

        const beforeArgs: GridOnCUDBeforeArgs = {
            requestParam: ajaxParam,
            handle: false
        }

        YvEventDispatch(option.onUpdateBefore, this.ctl, beforeArgs)
        if (beforeArgs.handle === true) {
            // 不允许请求
            param.node.cstate = 'same'
            return;
        }

        let ajaxPromise = <Promise<Db.Response>>brokerInvoke(YvanUI.getServerPrefix(serverUrl), method, ajaxParam)

        //异步处理数据内容
        this.ctl.loading = true
        const that = this
        ajaxPromise.then(res => {
            YvEventDispatch(option.onUpdateAfter, that.ctl, res)
            if (res.success) {
                that.ctl._acceptChanges(param.node)
            }
        }).catch(r => {
            YvanMessage.msg('修改失败')
        }).finally(() => {
            this.ctl.loading = false
        })
    }

    _addRow(param: any) {
        const option: any = this.option
        if (!option.createPath) {
            param.node.cstate = 'same'
            return;
            // throw new Error('no createPath')
        }
        const [serverUrl, method] = _.split(option.createPath, '@');

        const newData = {}
        _.forEach(this.ctl.vjson.columns, column => {
            if (column.field != this.ctl.idField) {
                const newValue = _.get(param.data, column.field)
                if (!newValue) {
                    return
                }
                if (column.editMode === "date" || column.editMode === 'datetime') {
                    let formatter = column.formatter
                    if (!YvanUI.formatter.hasOwnProperty(column.formatter)) {
                        console.error('没有发现全局函数 YvanUI.formatter.' + column.formatter)
                        _.set(newData, column.field, newValue)
                    } else {
                        formatter = YvanUI.formatter[column.formatter]
                        const dateValue = formatter(newValue)
                        if (dateValue) {
                            _.set(newData, column.field, dateValue)
                        } else {
                            _.set(newData, column.field, newValue)
                        }
                    }
                } else {
                    if (newValue) {
                        _.set(newData, column.field, newValue)
                    }
                }
            }
        })
        if (_.size(newData) <= 0) {
            param.node.cstate = 'same'
            return
        }
        const ajaxParam = {
            args: [newData]
        }

        const beforeArgs: GridOnCUDBeforeArgs = {
            requestParam: ajaxParam,
            handle: false
        }

        YvEventDispatch(option.onCreateBefore, this.ctl, beforeArgs)
        if (beforeArgs.handle === true) {
            // 不允许请求
            param.node.cstate = 'same'
            return;
        }

        let ajaxPromise = <Promise<Db.Response>>brokerInvoke(YvanUI.getServerPrefix(serverUrl), method, ajaxParam)

        //异步处理数据内容
        this.ctl.loading = true
        const that = this
        ajaxPromise.then(res => {
            YvEventDispatch(option.onCreateAfter, that.ctl, res)
            if (res.success) {
                _.set(param.data, that.ctl.idField, res.data)
                delete param.data.__grid_row_new
                that.ctl._acceptChanges(param.node)
                that.ctl._transactionUpdate(that.ctl._gridData)
            }
        }).catch(r => {
            YvanMessage.msg('新增失败')
        }).finally(() => {
            this.ctl.loading = false
        })
    }

    _deleteRow(param: any) {
        const option: any = this.option
        if (!option.deletePath) {
            param.node.cstate = 'same'
            return;
            // throw new Error('no deletePath')
        }
        const deleteId = this.ctl._getIdByRow(param.data)
        const [serverUrl, method] = _.split(option.deletePath, '@')
        const ajaxParam = {
            args: [deleteId]
        }

        const beforeArgs: GridOnCUDBeforeArgs = {
            requestParam: ajaxParam,
            handle: false
        }

        YvEventDispatch(option.onDeleteBefore, this.ctl, beforeArgs)
        if (beforeArgs.handle === true) {
            // 不允许请求
            param.node.cstate = 'same'
            return;
        }

        let ajaxPromise = <Promise<Db.Response>>brokerInvoke(YvanUI.getServerPrefix(serverUrl), method, ajaxParam)

        //异步处理数据内容
        this.ctl.loading = true
        const that = this
        ajaxPromise.then(res => {
            YvEventDispatch(option.onDeleteAfter, that.ctl, res)
            if (res.success) {
                that.ctl.reload()
            }
        }).catch(r => {
            YvanMessage.msg('删除失败')
        }).finally(() => {
            this.ctl.loading = false
        })
    }
}
